package net.towee.client.managers.impl;

import net.towee.client.event.core.BoardCreatedEvent;
import net.towee.client.event.core.BoardRemovedEvent;
import net.towee.client.event.core.BoardRenamedEvent;
import net.towee.client.event.core.GadgetAttached;
import net.towee.client.event.core.GroupAttachedToBoardEvent;
import net.towee.client.event.core.GroupDetachedFromBoardEvent;
import net.towee.client.event.core.UserLeaveFromGroupEvent;
import net.towee.client.event.core.UserLeaveFromGroupHandler;
import net.towee.client.gadgets.GadgetsHolder;
import net.towee.client.gadgets.ads.AdsGadgetInfo;
import net.towee.client.gadgets.recentdocs.RecentDocumentsGadgetInfo;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.DashBoardManager;
import net.towee.model.account.Board;
import net.towee.model.account.Gadget;
import net.towee.model.group.GroupIdentity;
import net.towee.server.rpc.services.ToweeServiceAsync;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;

public class DashBoardManagerImpl implements DashBoardManager {
	// Injected
	private final ToweeServiceAsync service;
	private final AccountHelper accountHelper;
	private final HandlerManager eventBus;

	@Inject
	public DashBoardManagerImpl(ToweeServiceAsync service, AccountHelper accountHelper,
			HandlerManager eventBus) {
		this.service = service;
		this.accountHelper = accountHelper;
		this.eventBus = eventBus;
		
		addHandlers();
	}

	private void addHandlers() {
		/*
		 * Handle leave user from group
		 */
		eventBus.addHandler(UserLeaveFromGroupEvent.TYPE, new UserLeaveFromGroupHandler() {
			@Override
			public void onUserLeaveGroup(GroupIdentity identity) {
				for(Board b : accountHelper.getAccount().getBoardsContainer().getBoardsList())
					if(b.hasAttachedGroup( identity.getId() )) {
						b.detachGroup( identity );
						eventBus.fireEvent(new GroupDetachedFromBoardEvent( identity.getId(), b ));
					}
			}
		});
	}

	/**
	 * Initialize available gadgets. FIXME: maybe some other way to initialize
	 * gadgets
	 * 
	 * @param injector
	 */
	@Inject
	@SuppressWarnings("unused")
	private void initGadgets(RecentDocumentsGadgetInfo rd, AdsGadgetInfo ad) {
		GadgetsHolder.addGadget(rd);
		GadgetsHolder.addGadget(ad);
	}

	@Override
	public void addBoard(Board board, final AsyncCallback<Board> handler) {
		Preconditions.checkNotNull(board, "null board");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(board.getName()), "null board name");

		service.addBoard(board, new AsyncCallback<Board>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Board result) {
				// Add board to local account
				accountHelper.getAccount().getBoardsContainer().addBoard(result);
				
				// Fire an event
				eventBus.fireEvent(new BoardCreatedEvent(result));
				
				// Invoke handler
				handler.onSuccess(result);
			}
		});
	}

	@Override
	public void removeBoard(final String boardId, final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");

		service.removeBoard(boardId, new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "fail remove board");

				// Get removed board
				Board board = accountHelper.getAccount().getBoardsContainer().getBoard(boardId);

				// Remove board from local account
				accountHelper.getAccount().getBoardsContainer().removeBoard(board);

				// Fire an event
				eventBus.fireEvent(new BoardRemovedEvent(board));

				// Invoke success method
				handler.onSuccess(true);
			}
		});
	}

	@Override
	public void renameBoard(final String boardId, final String newName,
			final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(newName), "null board new name");

		service.renameBoard(boardId, newName, new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "fail board rename");

				// Get old board
				Board board = accountHelper.getAccount().getBoardsContainer().getBoard(boardId);
				board.setName(newName);

				// Fire an event and ivoke handler
				eventBus.fireEvent(new BoardRenamedEvent(board));
				handler.onSuccess(true);
			}

		});
	}

	@Override
	public void attachGroupToBoard(final String boardId, final String groupId,
			final AsyncCallback<GroupIdentity> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(groupId), "null group id");
		Preconditions.checkArgument(accountHelper.isMemeberOfGroups(groupId), "user is not in group");

		service.attachGroupToBoard(boardId, groupId, new AsyncCallback<GroupIdentity>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(GroupIdentity result) {
				Preconditions.checkNotNull(result);

				// Add group to local account
				accountHelper.getAccount().getBoardsContainer().getBoard(boardId).attachGroup(result);

				// Fire an event
				eventBus.fireEvent(new GroupAttachedToBoardEvent(result));
				handler.onSuccess(result);
			}
		});
	}

	@Override
	public void detachGroupFromBoard(final String boardId, final String groupId,
			final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(groupId), "null group id");
		Preconditions.checkArgument(accountHelper.isMemeberOfGroups(groupId), "user is not in group");

		service.detachGroupFromBoard(boardId, groupId, new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "fail detach group");

				// Detach group from local stored account
				Board b = accountHelper.getAccount().getBoardsContainer().getBoard(boardId);
				b.detachGroup(groupId);

				// Fire an event and invoke handler
				eventBus.fireEvent(new GroupDetachedFromBoardEvent(groupId, b));
				handler.onSuccess(true);
			}

		});
	}

	@Override
	public void updateBoard(final Board currentBoard, final AsyncCallback<Board> callback) {
		Preconditions.checkNotNull(currentBoard, "null board");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(currentBoard.getId()), "null board id");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(currentBoard.getName()), "null board name");

		service.updateBoard(currentBoard, new AsyncCallback<Void>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Void result) {
				// Fire an event and invoke handler
				eventBus.fireEvent(new BoardCreatedEvent(currentBoard));				
				callback.onSuccess(currentBoard);
			}
		});
	}

	@Override
	public void attachGadgetToBoard(final String boardId, Gadget g, final AsyncCallback<Gadget> handler) {
		Preconditions.checkNotNull(g, "null gadget");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(g.getGadgetName()), "null gadget name");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");

		service.attachGadgetToBoard(boardId, g, new AsyncCallback<Gadget>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Gadget result) {
				Preconditions.checkNotNull(result, "fail attach gadget");
				
				// Attach gadget to board
				accountHelper.getAccount().getBoardsContainer().getBoard(boardId).addGadget(result);
				
				// Fire an event
				eventBus.fireEvent(new GadgetAttached(result, accountHelper.getAccount()
						.getBoardsContainer().getBoard(boardId)));
				
				// Invoke handler
				handler.onSuccess(result);
			}
		});
	}

	@Override
	public void detachGadgetFromBoard(final String boardId, final String gadgetId, final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(gadgetId), "null gadget id");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");
		
		service.detachGadgetFromBoard(boardId, gadgetId, new AsyncCallback<Boolean>(){

			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkNotNull(result, "fail detach gadget");
				
				// Attach gadget to board
				accountHelper.getAccount().getBoardsContainer().getBoard(boardId).deleteGadget(gadgetId);
				
				// Invoke handler
				handler.onSuccess(result);
			}
			
		});
	}

	@Override
	public void saveGadget(String boardId, Gadget g, final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(boardId), "null board id");
		Preconditions.checkNotNull(g, "null gadget for updating");
		
		service.updateGadget(boardId, g, handler);
	}
}
